#!/bin/sh
# Produce spectral analysis of lossless files and optionally upload
# generated pictures to image hosting service of your choice.
ptpapi=""
usage() {
  cat >&2 <<EOF
usage: ${0##*/} [-u ptpimg|catbox] <dir> [<dir2> <dir3>...]
EOF
  exit 1
}
err() { printf >&2 '%s: error: %s\n' "${0##*/}" "$*"; exit 2; }
msg() { printf '%s: %s\n' "${0##*/}" "$*"; }
chkutils() {
  for util in sox curl; do
    hash "$util" 2>/dev/null || err "$util is not installed!"
  done
}
chkopts() {
  while getopts :u: opt; do
    case "$opt" in
      u)
        case "$OPTARG" in
          catbox) service="catbox.moe" ;;
          ptpimg)
            if test -n "$ptpapi"; then
              service="ptpimg.me"
            else
              err "invalid ptpimg API key!"
            fi
            ;;
          *) usage ;;
        esac
        ;;
      :) err "-${OPTARG} requires an argument!" ;;
     \?|*) usage ;;
    esac
  done
}
mkspec() {
  c="Created by ${0##*/} & SoX with love"
  find "$arg" -type f -iname "*.flac" | while read -r f; do
    msg "creating full-length spectrogram of ${f##*/}..."
    out="${f%.*}.png"
    sox "$f" -n remix 1 spectrogram -x 3000 -y 513 -z 120 \
      -t "${f##*/}" -c "$c" -w kaiser -o "$out"
    mv "$out" "${f%/*}/specs/full/"
    msg "creating zoomed-in spectrogram of ${f##*/}..."
    duration="$(soxi -D "$f" | cut -d. -f1)"
    # soxi -D returns zero value if duration cannot be determined.
    case "$((
      duration >  0 && duration >= 75 ? 1 :
      duration >  0 && duration <= 15 ? 2 :
      duration > 15 && duration <= 30 ? 3 :
      duration > 30 && duration <= 50 ? 4 :
      duration > 50 && duration <= 75 ? 5 : 0
    ))" in
      1) sox "$f" -n remix 1 spectrogram -x 500 -y 1025 -z 120 \
           -t "${f##*/}" -c "$c" -w kaiser -S 1:00 -d 0:02 -o "$out" ;;
      2) sox "$f" -n remix 1 spectrogram -x 500 -y 1025 -z 120 \
           -t "${f##*/}" -c "$c" -w kaiser -S 0:05 -d 0:02 -o "$out" ;;
      3) sox "$f" -n remix 1 spectrogram -x 500 -y 1025 -z 120 \
           -t "${f##*/}" -c "$c" -w kaiser -S 0:15 -d 0:02 -o "$out" ;;
      4) sox "$f" -n remix 1 spectrogram -x 500 -y 1025 -z 120 \
           -t "${f##*/}" -c "$c" -w kaiser -S 0:25 -d 0:02 -o "$out" ;;
      5) sox "$f" -n remix 1 spectrogram -x 500 -y 1025 -z 120 \
           -t "${f##*/}" -c "$c" -w kaiser -S 0:35 -d 0:02 -o "$out" ;;
      0) err "the duration of $f is unknown! aborting..." ;;
    esac
    mv "$out" "${f%/*}/specs/zoomed/"
  done
}
u2p() {
  url="https://ptpimg.me/upload.php"
  printf >> "${pdir%/*}/description.txt" \
    '%s\n[img=500]%s[/img]\n' "${i##*/}" "$(
      curl -s -F "file-upload[0]=@${i}" -F "api_key=${ptpapi}" "$url" |
        awk 'BEGIN { FS="\"" }
          /"code"/ { c = $4 }
          /"ext"/  { e = $4 }
          END      { print "https://ptpimg.me/" c "." e }'
    )"
}
u2c() {
  url="https://catbox.moe/user/api.php"
  printf >> "${pdir%/*}/description.txt" \
    '%s\n[img=500]%s[/img]\n' "${i##*/}" "$(
      curl -s -F "reqtype=fileupload" -F "fileToUpload=@${i}" "$url"
    )"
}
upload() {
  find "$arg" -path "*specs/full/*.png" | sort | while read -r i; do
    pdir="${i%/*}"
    pdir="${pdir%/*}"
    msg "uploading ${i##*/} to ${service}..."
    case "$service" in
      ptpimg.me) u2p ;;
      catbox.moe) u2c ;;
    esac
  done
}
aptags() {
  # Append and prepend BBCode-compliant tags.
  find "$arg" -type f -name "description.txt" -exec sed -i \
    -e '1s|^|[spoiler=Spectrograms][align=center]|' \
    -e '$s|$|[/align][/spoiler]|' {} +
}
main() {
  chkutils; chkopts "$@" && shift $((OPTIND - 1))
  test $# -eq 0 && usage
  for arg; do
    test -d "$arg" || err "$arg is not a directory!"
    find "$arg" -type f -iname "*.flac" | while read -r flac; do
      mkdir -p "${flac%/*}/specs/full" "${flac%/*}/specs/zoomed"
    done
    mkspec "$arg"
    test -n "$service" && upload "$arg"
    aptags "$arg"
  done
  if test -n "$service"; then
    msg "formatted output can be found here:"
    find "$@" -type f -name "description.txt" | while read -r file; do
      printf '%10s%2s./%s\n' ">" " " "$file"
    done
  fi
  msg "done!"
}
